﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using LogoFX.Controls.Internal;

namespace LogoFX.Controls
{
    /// <summary>
    /// A control containing two TreeViews, allowing the dock one side and to freely scroll the other, without scrolling the docked tree.
    /// </summary>
    public class StackedTreeViewControl : Control
    {
        #region CTOR

#if SILVERLIGHT
        static StackedTreeViewControl()
        {
            
        }
#else
        public StackedTreeViewControl()
        {
            DefaultStyleKey = s_thisType;
            Loaded += StackedTreeViewControl_Loaded;
        }
#endif

        #endregion

        #region Fields

        private static Type s_thisType = typeof(StackedTreeViewControl);
        private DockedTreeView ContentTree, DockedTree;
        private ScrollViewer ContentScrollViewer, DockedScrollViewer;
        private ScrollBar contentScrollbar, ContentVerticalScroll, dockedScrollbar;
        private Binding HorizontalVisibilityBinding;

        #endregion

        #region overrides

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            ContentTree = GetTemplateChild("ScrollableContentTree_PART") as DockedTreeView;
            DockedTree = GetTemplateChild("DockedFlatTree_PART") as DockedTreeView;

        }



        #endregion

        #region Event Handlers

        private void StackedTreeViewControl_Loaded(object sender, RoutedEventArgs e)
        {

            if (ContentTree == null || DockedTree == null) return;

            ContentTree.ItemsSource = ItemsSource;
            ContentScrollViewer = ContentTree.GetVisualDescendent<ScrollViewer>();
            DockedScrollViewer = DockedTree.GetVisualDescendent<ScrollViewer>();
            ContentVerticalScroll = ContentTree.GetVisualDescendent<ScrollBar>();

            //Todo: all of the referenced items (eg. scrollbars, scrollviewers etc) should be named from the template and referenced as getTemplateChild and registered as template parts
            if (ContentVerticalScroll != null)
            {
                {
                    ContentVerticalScroll.GetVisualSiblingsAndSelf().ForEach(parameter =>
                    {
                        if (parameter is ScrollBar && (parameter as ScrollBar).Name == "HorizontalScrollBar")
                        {
                            HorizontalVisibilityBinding = new Binding
                            {
                                Mode = BindingMode.OneWay,
                                Source = parameter,
                                Path = new PropertyPath("Visibility"),
                                Converter = new HorizontalScrollVisibilityToMarginConverter(),
                                ConverterParameter = (parameter as ScrollBar).Height
                            };
                            contentScrollbar = parameter as ScrollBar;
                        }
                    });
                }


                ContentVerticalScroll.ValueChanged += ContentVerticalScroll_ValueChanged;
            }
            if (HorizontalVisibilityBinding!=null)
            DockedTree.SetBinding(MarginProperty, HorizontalVisibilityBinding);
            dockedScrollbar = DockedTree.GetVisualDescendent<ScrollBar>();
            dockedScrollbar.Scroll += verticalScrollBar_Scroll;
            dockedScrollbar.ValueChanged += dockedScrollbar_ValueChanged;
        }

        private void verticalScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            ContentScrollViewer.ScrollToVerticalOffset(e.NewValue);
        }


        public void treeViewItem_Collapsed(object sender, RoutedEventArgs e)
        {
            if (!(sender is TreeViewItem)) return;

            TreeViewItem collapsedItem = sender as TreeViewItem;

            collapsedItem.GetContainers().ForEach(treeViewItem =>
                                                      {
                                                          treeViewItem.Expanded -= treeViewItem_Expanded;
                                                          treeViewItem.Collapsed -= treeViewItem_Collapsed;
                                                      });

            DockedTree.GetContainerFromItem(collapsedItem.DataContext).IsExpanded = false;
        }

        public void treeViewItem_Expanded(object sender, RoutedEventArgs e)
        {
            if (!(sender is TreeViewItem)) return;
            TreeViewItem expandedItem = sender as TreeViewItem;
            DockedTree.GetContainerFromItem(expandedItem.DataContext).ItemsSource = expandedItem.ItemsSource;
            DockedTree.GetContainerFromItem(expandedItem.DataContext).IsExpanded = true;
            expandedItem.LayoutUpdated += (s, args) => expandedItem.GetContainers().ForEach(treeViewItem =>
                                                                                                {
                                                                                                    treeViewItem.
                                                                                                        Expanded -=
                                                                                                        treeViewItem_Expanded;
                                                                                                    treeViewItem.
                                                                                                        Expanded +=
                                                                                                        treeViewItem_Expanded;
                                                                                                    treeViewItem.
                                                                                                        Collapsed -=
                                                                                                        treeViewItem_Collapsed;
                                                                                                    treeViewItem.
                                                                                                        Collapsed +=
                                                                                                        treeViewItem_Collapsed;
                                                                                                });

        }


        public void HandleSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if ((sender as DockedTreeView).Name == "ScrollableContentTree_PART")
                RealContentWidth = e.NewSize.Width;

            if ((sender as DockedTreeView).Name == "DockedFlatTree_PART")
                RealDockedWidth = e.NewSize.Width;


            double margin = 0;
            margin += RealDockedWidth;

            if (DockedScrollViewer != null && DockedScrollViewer.ComputedVerticalScrollBarVisibility == Visibility.Visible)
                margin += 17;

            scrollerMargin = margin;
        }


        private void ContentVerticalScroll_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            DockedScrollViewer.ScrollToVerticalOffset(e.NewValue);
        }

        private void dockedScrollbar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            ContentScrollViewer.ScrollToVerticalOffset(e.NewValue);
        }

        #endregion

        #region scrollbar margin property



        public static readonly DependencyProperty scrollerMarginProperty =
          DependencyProperty.Register("scrollerMargin", typeof(double), s_thisType, new PropertyMetadata((double)0, OnScrollerMarginChanged));

        private static void OnScrollerMarginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = d as StackedTreeViewControl;
            if (null != control)
            {
                Thickness newMargin = new Thickness(0, 0, 0 - ((double)e.NewValue), 0);
                if (e.NewValue is double && control.contentScrollbar != null)
                {
                    if (null != control.DockedScrollViewer && control.DockedScrollViewer.ComputedVerticalScrollBarVisibility == Visibility.Visible)
                        newMargin = new Thickness(0, 0, 0 - ((double)e.NewValue - control.dockedScrollbar.Width * 2 - 1), 0);
                    control.contentScrollbar.Margin = newMargin;
                }

            }

        }

        public double scrollerMargin
        {
            get { return (double)GetValue(scrollerMarginProperty); }
            private set { SetValue(scrollerMarginProperty, value); }

        }


        #endregion

        #region Real width properties

        public static readonly DependencyProperty RealDockedWidthProperty =
            DependencyProperty.Register("RealDockedWidth", typeof(double),
                                        s_thisType,
                                        new PropertyMetadata(500D));

        public double RealDockedWidth
        {
            get { return (double)GetValue(RealDockedWidthProperty); }
            set { SetValue(RealDockedWidthProperty, value); }
        }



        public static readonly DependencyProperty RealContentWidthProperty =
            DependencyProperty.Register("RealContentWidth", typeof(double),
                                        s_thisType,
                                        new PropertyMetadata(500D));

        public double RealContentWidth
        {
            get { return (double)GetValue(RealContentWidthProperty); }
            set { SetValue(RealContentWidthProperty, value); }
        }
        #endregion

        #region ItemContainerStyle



        public Object ContentItemContainerStyle
        {
            get { return (Object)GetValue(ContentItemContainerStyleProperty); }
            set { SetValue(ContentItemContainerStyleProperty, value); }
        }

        public static readonly DependencyProperty ContentItemContainerStyleProperty =
            DependencyProperty.Register("ContentItemContainerStyle", typeof(Object), s_thisType, null);

        public Object DockedItemContainerStyle
        {
            get { return (Object)GetValue(DockedItemContainerStyleProperty); }
            set { SetValue(DockedItemContainerStyleProperty, value); }
        }

        public static readonly DependencyProperty DockedItemContainerStyleProperty =
            DependencyProperty.Register("DockedItemContainerStyle", typeof(Object), s_thisType, null);

        #endregion

        #region ItemTemplate

        public DataTemplate ContentItemTemplate
        {
            get { return (DataTemplate)GetValue(ContentItemTemplateProperty); }
            set { SetValue(ContentItemTemplateProperty, value); }
        }

        public static readonly DependencyProperty ContentItemTemplateProperty =
            DependencyProperty.Register("ContentItemTemplate", typeof(DataTemplate), s_thisType, null);


        public DataTemplate DockedItemTemplate
        {
            get { return (DataTemplate)GetValue(DockedItemTemplateProperty); }
            set { SetValue(DockedItemTemplateProperty, value); }
        }

        public static readonly DependencyProperty DockedItemTemplateProperty =
            DependencyProperty.Register("DockedItemTemplate", typeof(DataTemplate), s_thisType, null);

        #endregion

        #region ItemsPanelTemplate

        public ItemsPanelTemplate ContentItemsPanel
        {
            get { return (ItemsPanelTemplate)GetValue(ContentItemsPanelProperty); }
            set { SetValue(ContentItemsPanelProperty, value); }
        }

        public static readonly DependencyProperty ContentItemsPanelProperty =
            DependencyProperty.Register("ContentItemsPanelProperty", typeof(ItemsPanelTemplate), s_thisType, null);

        public ItemsPanelTemplate DockedItemsPanel
        {
            get { return (ItemsPanelTemplate)GetValue(DockedItemsPanelProperty); }
            set { SetValue(DockedItemsPanelProperty, value); }
        }

        public static readonly DependencyProperty DockedItemsPanelProperty =
            DependencyProperty.Register("DockedItemsPanelProperty", typeof(ItemsPanelTemplate), s_thisType, null);

        #endregion

        #region ItemsSource

        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable), s_thisType, null);


        #endregion



    }


    public class HorizontalScrollVisibilityToMarginConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Thickness newMargin = new Thickness(0);
            if (value is Visibility)
            {
                if ((Visibility)value == Visibility.Visible)
                {
                    newMargin = new Thickness(0, 0, 0, (double)parameter);
                }
            }
            return newMargin;
        }

        public object ConvertBack(object value, Type targetType, object parameter,
                                  System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class DockedTreeViewItem : TreeViewItem
    {
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.Expanded += this.FindVisualAncestor<StackedTreeViewControl>(true).treeViewItem_Expanded;
            this.Collapsed += this.FindVisualAncestor<StackedTreeViewControl>(true).treeViewItem_Collapsed;
        }
    }

    public class DockedTreeView : System.Windows.Controls.TreeView
    {
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            SizeChanged += this.FindVisualAncestor<StackedTreeViewControl>(true).HandleSizeChanged;

        }
#if !SILVERLIGHT
        public TreeViewItem GetContainerFromItem(object item)
        {
            return (TreeViewItem)VisualAndLogicalTreesHelpers.GetContainerFromItem(this,item);
        }
#endif

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new DockedTreeViewItem();
        }
    }
}